#ifndef ___fat32_cache_intercambiar_bloques

#define ___fat32_cache_intercambiar_bloques 1

#include "f32_cchd.c"
#include "hora.c"
#include <math.h>
#include "operbits.c"
#include <stdlib.h>

const unsigned char fat32_cantidad_escrituras_por_estado[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
const long double fat32_puntajes_por_escrituras[5] = {0.0L, 1.0L, 2.5L, 4.0L, 7.0L};

void fat32_agregar_bloque_a_cache(unsigned long, void *);
unsigned char fat32_bloque_cache_expulsado(void);
void fat32_expulsar_bloque_cache(unsigned char);
long double fat32_puntaje_bloque_cache(unsigned char, long double);
long double fat32_contribucion_temporal_puntaje(long double, long double);

void fat32_agregar_bloque_a_cache (unsigned long bloque, void * datosBloque) {
  if (!fat32_cache.cantidadBloques) return;
  unsigned char numBloque = fat32_bloque_cache_expulsado();
  fat32_expulsar_bloque_cache(numBloque);
  fat32_datos_control_cache nuevoBloque;
  nuevoBloque.sectorInicial = bloque;
  nuevoBloque.cantidadAccesos = 1;
  nuevoBloque.insercion = hora();
  nuevoBloque.acceso = hora();
  nuevoBloque.estado = 0;
  fat32_cache.control[numBloque] = nuevoBloque;
  memcpy(fat32_cache.datos + (2048 * numBloque), datosBloque, 2048);
}

unsigned char fat32_bloque_cache_expulsado (void) {
  long double minimoValor = HUGE_VALL;
  unsigned char bloqueMinimo = 255;
  long double p;
  unsigned char x;
  long double horaActual = hora();
  for (x = 0; x < fat32_cache.cantidadBloques; x ++) {
    if (fat32_cache.control[x].estado == 255) return x;
    p = fat32_puntaje_bloque_cache(x, horaActual);
    if (p < minimoValor) {
      minimoValor = p;
      bloqueMinimo = x;
    }
  }
  return bloqueMinimo;
}

void fat32_expulsar_bloque_cache (unsigned char numeroBloque) {
  unsigned long sectores[4];
  char datosSectores[4][512];
  unsigned char escribirSector[4];
  unsigned char posicion;
  for (posicion = 0; posicion < 4; posicion ++) {
    sectores[posicion] = posicion + fat32_cache.control[numeroBloque].sectorInicial;
    memcpy(datosSectores[posicion], fat32_cache.datos + (2048 * numeroBloque) + (512 * posicion), 512);
    escribirSector[posicion] = obtenerBits(fat32_cache.control[numeroBloque].estado, posicion, 1);
  }
  for (posicion = 0; posicion < 4; posicion ++)
    if (escribirSector[posicion]) {
      fat32_sector_pendiente_escritura * ds = malloc(sizeof(fat32_sector_pendiente_escritura));
      ds -> siguiente = fat32_lista_pendientes_escritura;
      ds -> sector = sectores[posicion];
      memcpy(ds -> datos, datosSectores[posicion], 512);
      fat32_lista_pendientes_escritura = ds;
    }
  fat32_cache.control[numeroBloque].estado = 255;
}

long double fat32_puntaje_bloque_cache (unsigned char numeroBloque, long double horaActual) {
  long double contribucionTemporal, contribucionEscrituras, contribucionAccesos;
  long double ct1, ct2;
  long double valorTotal;
  unsigned char escrituras;
  ct1 = fat32_contribucion_temporal_puntaje(fat32_cache.control[numeroBloque].acceso, horaActual);
  ct2 = fat32_contribucion_temporal_puntaje(fat32_cache.control[numeroBloque].insercion, horaActual);
  contribucionTemporal = (4 * ct1 + ct2) / 5;
  escrituras = fat32_cantidad_escrituras_por_estado[fat32_cache.control[numeroBloque].estado];
  contribucionEscrituras = fat32_puntajes_por_escrituras[escrituras];
  contribucionAccesos = 0.01L * (fat32_cache.control[numeroBloque].cantidadAccesos * fat32_cache.control[numeroBloque].cantidadAccesos);
  valorTotal = contribucionTemporal + contribucionEscrituras + contribucionAccesos;
  return valorTotal;
}

long double fat32_contribucion_temporal_puntaje (long double horaEvento, long double horaActual) {
  long double diferenciaTemporal, contribucionTemporal;
  diferenciaTemporal = horaActual - horaEvento;
  contribucionTemporal = 5 * expl(-25 * diferenciaTemporal);
  return contribucionTemporal - 10 * diferenciaTemporal;
}

#endif